package com.ruoyi.redis;

import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.ip.IpUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Collections;

@Aspect
@Component("redisRateLimiterAspect1")
public class RateLimiterAspect1 {
    private final RedisTemplate<Object, Object> redisTemplate;
    private final DefaultRedisScript<Long> limitScript;

    @Autowired
    public RateLimiterAspect1(RedisTemplate<Object, Object> redisTemplate,
                              DefaultRedisScript<Long> limitScript) {
        this.redisTemplate = redisTemplate;
        this.limitScript = limitScript;
    }

    @Pointcut("@annotation(com.ruoyi.redis.RateLimiter)")
    public void rateLimitPointcut() {
    }

    @Around("rateLimitPointcut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        RateLimiter rateLimiter = method.getAnnotation(RateLimiter.class);

        // 生成限流key
        String key = generateKey(rateLimiter, method);

        // 执行限流脚本
        Long count = redisTemplate.execute(
                limitScript,
                Collections.singletonList(key),
                rateLimiter.count(),
                rateLimiter.time()
        );

        // 超过阈值则限流
        if (count != null && count > rateLimiter.count()) {
            throw new RateLimitException(rateLimiter.message());
        }

        return point.proceed();
    }

    /**
     * 生成限流Key（服务名+方法名+IP）
     */
    private String generateKey(RateLimiter rateLimiter, Method method) {
        StringBuilder key = new StringBuilder(rateLimiter.key());

        // 添加服务名和方法名
        key.append(method.getDeclaringClass().getSimpleName())
                .append(":")
                .append(method.getName());

        // 添加客户端IP
        String clientIp = IpUtils.getIpAddr(ServletUtils.getRequest());
        if (StringUtils.isNotBlank(clientIp)) {
            key.append(":").append(clientIp);
        }

        return key.toString();
    }
}
